﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Reflection;

namespace System.Collections.Generic
{
    public class TreeNode<T> where T : class
    {
        public T Content;
        public List<TreeNode<T>> Nodes = new List<TreeNode<T>>();
        public void AddRange(IEnumerable<T> items)
        {
            foreach (var item in items)
                Add(item);
        }
        public void Add(T item)
        {
            Nodes.Add(new TreeNode<T>() { Content = item });
        }
        public void Remove(T item)
        {
            Nodes.RemoveAll(s => s.Content == item);
        }
        public void RemoveAt(int index)
        {
            Nodes.RemoveAt(index);
        }
        public T this[int index]
        {
            get
            {
                if (index >= 0 && Nodes.Count > 0 && index < Nodes.Count)
                    return Nodes[index].Content;
                else
                    return null;
            }
        }
        public IEnumerable<T> GetChilds()
        {
            foreach (var item in Nodes)
                yield return item.Content;
        }
        public IEnumerable<T> GetAllChilds()
        {
            foreach (var item in Nodes)
            {
                yield return item.Content;
                foreach (var item1 in item.Nodes
                    .SelectMany(s => s.GetAllChilds()))
                    yield return item1;
            }
        }
    }

    public class Tree<T> : TreeNode<T> where T : class
    {
        
    }
}

namespace System.Reflection
{
    public static class AssemblyExtension
    {
        public static Tattr GetAttribute<Tattr>(this Assembly assembly)
        {
            return (Tattr)assembly.GetCustomAttributes(typeof(Tattr), true).FirstOrDefault();
        }

        /// <summary>
        /// Получить генеалогическое дерево типов 
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public static Tree<Type> GetTreeTypes(this Assembly assembly)
        {
            Tree<Type> result = new Tree<Type>();
            result.AddRange(assembly.GetTypes().ToList());
            OrderTree(result);
            return result;
        }

        public static Tree<Type> GetTreeTypes(this Assembly assembly, Func<Type, bool> predicate)
        {                  
            Tree<Type> result = new Tree<Type>();
            result.AddRange(assembly.GetTypes().Where(predicate).ToList());
            OrderTree(result);
            return result;
        }

        public static void OrderTree(TreeNode<Type> node)
        {
            HashSet<TreeNode<Type>> lst = new HashSet<TreeNode<Type>>();
            for (int i = 0; i < node.Nodes.Count - 1; i++)
            {
                for (int j = i + 1; j < node.Nodes.Count; j++)
                {
                    if (node[i].IsSubclassOf(node[j]))
                    {
                        node.Nodes[j].Nodes.Add(node.Nodes[i]);
                        if (!lst.Contains(node.Nodes[i]))
                            lst.Add(node.Nodes[i]);
                    }
                    else if (node[j].IsSubclassOf(node[i]))
                    {
                        node.Nodes[i].Nodes.Add(node.Nodes[j]);
                        if (!lst.Contains(node.Nodes[j]))
                            lst.Add(node.Nodes[j]);
                    }
                }
            }
            foreach (var item in lst)            
                node.Nodes.Remove(item);
            foreach (var item in node.Nodes)            
                OrderTree(item);           
        }


    }
}
